package com.jiawa.train.business.service;
import com.jiawa.train.business.domain.Train;
import com.jiawa.train.business.mapper.TrainMapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BloomFilterService {

    private static final Logger LOG = LoggerFactory.getLogger(BloomFilterService.class);

    /**
     * 定义布隆过滤器的名称，以便在各处统一使用
     */
    public static final String TRAIN_CODE_BLOOM_FILTER = "business:train_code_bloom_filter";

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private TrainMapper trainMapper;

    /**
     * 项目启动后执行，初始化布隆过滤器
     */
    @PostConstruct
    public void initTrainCodeBloomFilter() {
        LOG.info("开始初始化车次代码布隆过滤器...");
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(TRAIN_CODE_BLOOM_FILTER);

        // 初始化过滤器：预计放入10000个元素，期望误判率为0.01%
        // tryInit只有在过滤器未创建时才会执行初始化，防止重复操作
        bloomFilter.tryInit(10000, 0.01);

        // 从数据库加载所有车次code
        List<Train> trainList = trainMapper.selectByExample(null);
        if (trainList != null) {
            for (Train train : trainList) {
                bloomFilter.add(train.getCode());
            }
        }
        LOG.info("车次代码布隆过滤器初始化完成，加载了 {} 个车次代码。", trainList != null ? trainList.size() : 0);
    }

    /**
     * 向布隆过滤器添加一个车次代码
     * @param trainCode 车次代码
     */
    public void add(String trainCode) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(TRAIN_CODE_BLOOM_FILTER);
        // 即使过滤器已存在，tryInit也不会有任何影响，但可以确保在意外清空后的第一次添加时能正确初始化
        bloomFilter.tryInit(10000, 0.01);
        bloomFilter.add(trainCode);
        LOG.info("新的车次代码 {} 已添加到布隆过滤器", trainCode);
    }

    /**
     * 检查一个车次代码是否存在于布隆过滤器中
     * @param trainCode 车次代码
     * @return 如果存在则返回 true
     */
    public boolean contains(String trainCode) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(TRAIN_CODE_BLOOM_FILTER);
        return bloomFilter.contains(trainCode);
    }
}
